﻿using System.Collections.Generic;
using System.Drawing;

namespace Engine.Helpers
{
    public class LineAlgorithmus
    {
        #region Public Methods

        /// <summary>
        ///
        ///--------------------------------------------------------------
        /// Bresenham-Algorithmus: Linien auf Rastergeräten zeichnen
        ///
        /// Eingabeparameter:
        ///    int xstart, ystart        = Koordinaten des Startpunkts
        ///    int xend, yend            = Koordinaten des Endpunkts
        ///
        /// Ausgabe:
        ///    void SetPixel(int x, int y) setze ein Pixel in der Grafik
        ///         (wird in dieser oder aehnlicher Form vorausgesetzt)
        ///---------------------------------------------------------------
        ///
        /// </summary>
        /// <param name="xstart"></param>
        /// <param name="ystart"></param>
        /// <param name="xend"></param>
        /// <param name="yend"></param>
        /// <returns></returns>
        public static List<Point> CreateLine(int xstart, int ystart, int xend, int yend)
        {
            {
                int x, y, t, dx, dy, incx, incy, pdx, pdy, ddx, ddy, es, el, err;
                List<Point> points = new List<Point>();

                // Entfernung in beiden Dimensionen berechnen */
                dx = xend - xstart;
                dy = yend - ystart;

                // Vorzeichen des Inkrements bestimmen */
                incx = sgn(dx);
                incy = sgn(dy);
                if (dx < 0) dx = -dx;
                if (dy < 0) dy = -dy;

                // feststellen, welche Entfernung größer ist */
                if (dx > dy)
                {
                    // x ist schnelle Richtung */
                    pdx = incx; pdy = 0;    /* pd. ist Parallelschritt */
                    ddx = incx; ddy = incy; /* dd. ist Diagonalschritt */
                    es = dy; el = dx;   /* Fehlerschritte schnell, langsam */
                }
                else
                {
                    // y ist schnelle Richtung */
                    pdx = 0; pdy = incy; /* pd. ist Parallelschritt */
                    ddx = incx; ddy = incy; /* dd. ist Diagonalschritt */
                    es = dx; el = dy;   /* Fehlerschritte schnell, langsam */
                }

                // Initialisierungen vor Schleifenbeginn */
                x = xstart;
                y = ystart;
                err = el / 2;
                points.Add(new Point(x, y));

                // Pixel berechnen */
                for (t = 0; t < el; ++t) /* t zaehlt die Pixel, el ist auch Anzahl */
                {
                    // Aktualisierung Fehlerterm */
                    err -= es;
                    if (err < 0)
                    {
                        /* Fehlerterm wieder positiv (>=0) machen */
                        err += el;
                        /* Schritt in langsame Richtung, Diagonalschritt */
                        x += ddx;
                        //points.Add(new Point(x, y));
                        y += ddy;
                    }
                    else
                    {
                        /* Schritt in schnelle Richtung, Parallelschritt */

                        y += pdy;
                        //points.Add(new Point(x, y));
                        x += pdx;
                    }
                    points.Add(new Point(x, y));
                }

                // Linienbreite verbreitern
                int addidionalLineWidth = 1;
                List<Point> newPointsWithWidth = new List<Point>();
                foreach (Point point in points)
                {
                    for (x = -addidionalLineWidth; x <= addidionalLineWidth; x++)
                    {
                        for (y = -addidionalLineWidth; y <= addidionalLineWidth; y++)
                        {
                            Point newPoint = new Point(point.X + x, point.Y + y);
                            if (!newPointsWithWidth.Contains(newPoint))
                                newPointsWithWidth.Add(newPoint);
                        }
                    }
                }

                return newPointsWithWidth;
            }
        }

        #endregion Public Methods

        #region Private Methods

        private static int sgn(int x)
        {
            return (x > 0) ? 1 : (x < 0) ? -1 : 0;
        }

        #endregion Private Methods
    }
}